package org.study.clickhouse.api.util;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import com.google.common.collect.Lists;
import lombok.Data;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * okhttp + clickhouse 示例。
 * <p>
 * 用户名和密码可以通过以下三种方式指定：
 * 1、通过HTTP Basic Authentication指定：http://user:password@localhost:8123
 * 2、通过URL参数中的user和password指定：http://localhost:8123/?user=user&password=password
 * 3、通过请求头参数X-ClickHouse-User、X-ClickHouse-Key指定：X-ClickHouse-User: user、X-ClickHouse-Key: password
 * 注意：如果未指定用户名，则使用default。如果未指定密码，则使用空密码。
 * 还可以使用URL参数来指定处理单个查询或整个设置配置文件的任何设置。如:http://localhost:8123/?profile=web&max_rows_to_read=1000000000&query=SELECT+1
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2023年05月10日
 */
public class ClickHouseMainTest4 {
    private static String CK_URL = "https://play.clickhouse.com";
    private static String CK_CLUSTER = "";
    private static String CK_DATABASE = "default";
    private static String CK_USER = "explorer";
    private static String CK_PASSWORD = "";
    private static OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(5, TimeUnit.SECONDS)
            .readTimeout(5, TimeUnit.SECONDS)
            .writeTimeout(5, TimeUnit.SECONDS)
            .build();

    public static void main(String[] args) {
        // 测试一：试试不加 FORMAT JSONEachRow 的效果
        System.out.println(doGet("show databases"));
        System.out.println(doGet("show tables"));
        System.out.println(doGet("desc hits"));
        System.out.println(doGet("select count(*) from hits"));
        String result = doGet("select * from hits limit 10");
        System.out.println(result);

        // 测试二：试试不加 FORMAT JSONEachRow 的效果
        List<Database> databaseList = getData("show databases", Database.class);
        System.out.println(databaseList);
        List<Table> tableList = getData("show tables", Table.class);
        System.out.println(tableList);
        List<TableDesc> tableDescList = getData("desc hits", TableDesc.class);
        System.out.println(tableDescList);
        List<Object> objectList = getData("select * from hits limit 10", Object.class);
        System.out.println(objectList);
    }

    @Data
    static class Database {
        private String name;
    }

    @Data
    static class Table {
        private String name;
    }

    @Data
    static class TableDesc {
        private String name;
        private String type;
        @JSONField(name = "default_type")
        private String defaultType;
        @JSONField(name = "default_expression")
        private String defaultExpression;
        private String comment;
        @JSONField(name = "codec_expression")
        private String codecExpression;
        @JSONField(name = "ttl_expression")
        private String ttlExpression;
    }

    /**
     * GET请求
     *
     * @param sql
     * @return java.lang.String
     * @throws
     **/
    public static String doGet(String sql) {
        long start = System.currentTimeMillis();
        String result = null;
        String queryUrl = CK_URL + "?database=" + CK_DATABASE + "&query=" + sql + " FORMAT JSONEachRow";
        Request request = new Request.Builder()
                .url(queryUrl)
                .get()
                .addHeader("content-type", "application/json")
                .addHeader("cache-control", "no-cache")
                .addHeader("X-ClickHouse-User", CK_USER)
                .addHeader("X-ClickHouse-Key", CK_PASSWORD)
                .build();
        try (Response response = client.newCall(request).execute()) {
            ResponseBody responseBody = response.body();
            result = responseBody.string();
        } catch (IOException ex) {
            System.err.println("从ClickHouse中查询数据异常，SQL语句：" + sql + "，异常信息：" + ex);
        }
        System.out.println("执行SQL语句【" + sql + "】耗时：" + (System.currentTimeMillis() - start) + "ms");
        return result;
    }

    /**
     * JSON形式POST请求
     *
     * @param sql
     * @return java.lang.String
     * @throws
     **/
    public static String doJsonPost(String sql) {
        long start = System.currentTimeMillis();
        String result = null;
        MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(mediaType, sql + " FORMAT JSONEachRow");
        Request request = new Request.Builder()
                .url(CK_URL + "?database=" + CK_DATABASE)
                .post(body)
                .addHeader("content-type", "application/json")
                .addHeader("cache-control", "no-cache")
                .addHeader("X-ClickHouse-User", CK_USER)
                .addHeader("X-ClickHouse-Key", CK_PASSWORD)
                .build();
        try (Response response = client.newCall(request).execute()) {
            ResponseBody responseBody = response.body();
            result = responseBody.string();
        } catch (IOException ex) {
            System.err.println("从ClickHouse中查询数据异常，SQL语句：" + sql + "，异常信息：" + ex);
        }
        System.out.println("执行SQL语句【" + sql + "】耗时：" + (System.currentTimeMillis() - start) + "ms");
        return result;
    }

    public static <T> List<T> getData(String sql, Class<T> dataClass) {
        long start = System.currentTimeMillis();
        List<T> list = Lists.newArrayList();
        String response = doJsonPost(sql);
        System.out.println("响应结果：" + response);
        if (StringUtils.isNotBlank(response)) {
            list = Arrays.stream(response.split("\n"))
                    .filter(line -> StringUtils.isNotBlank(line))
                    .map(row -> JSONObject.parseObject(row, dataClass))
                    .collect(Collectors.toList());
            /*JSONArray jsonArray = JSONArray.parseArray("[" + response + "]");
            List<T> parseArray = JSONArray.parseArray("[" + response + "]", dataClass);*/
        }
        System.out.println("执行SQL语句【" + sql + "】耗时：" + (System.currentTimeMillis() - start) + "ms");
        return list;
    }
}
